#ifndef _CONF_H
#define	_CONF_H

#include <QTextStream>
#include <QStringList>
#include <QPair>
#include <QList>

#include <edgy/configdata.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>

/** getKeys() return type. */
struct keylist {
  KeySym k1;
  KeySym k2;
  KeySym k3;
};

/**
 * Represents a menu item in the GUI. Should only be used as a base class;
 * don't use this directly.
 */
class ConfigMenuItem {
public:
  virtual ~ConfigMenuItem();
  virtual void type() = 0; // for polymorphism
};

/**
 * Represents a key action menu item in the GUI.
 */
class ConfigMenuItem_Key : public ConfigMenuItem {
public:
  ConfigMenuItem_Key(QString label, QString k1, QString k2, QString k3, const Place* p);
  void type();

  QString label;
  QString k1, k2, k3;
  const Place* p;
};

/**
 * Represents a command action menu item in the GUI.
 */
class ConfigMenuItem_Cmd : public ConfigMenuItem {
public:
  ConfigMenuItem_Cmd(QString label, QString cmd);
  void type();

  QString label, cmd;
};

/**
 * Represents a "no action" action menu item in the GUI.
 */
class ConfigMenuItem_NoAction : public ConfigMenuItem {
public:
  ConfigMenuItem_NoAction(QString label);
  void type();

  QString label;
};

/**
 * Represents an effect item in the GUI. Don't use directly; derive from this.
 */
class ConfigEffectItem {
public:
  virtual ~ConfigEffectItem();
  virtual void type() = 0; // for polymorphism
};
/**
 * Represents a key action effect item in the GUI.
 */
class ConfigEffectItem_Key : public ConfigEffectItem {
public:
  ConfigEffectItem_Key(QString k1, QString k2, QString k3, const Place* p);
  void type();

  QString k1, k2, k3;
  const Place* p;
};
/**
 * Represents a stateful key action effect item in the GUI.
 */
class ConfigEffectItem_SKey : public ConfigEffectItem {
public:
  ConfigEffectItem_SKey(QString m, QString b1, QString k1, QString b2, QString k2);
  void type();

  QString m, b1, k1, b2, k2;
};
/**
 * Represents a command action effect item in the GUI.
 */
class ConfigEffectItem_Cmd : public ConfigEffectItem {
public:
  ConfigEffectItem_Cmd(QString cmd);
  void type();

  QString cmd;
};
/**
 * Represents a vertical menu action effect item in the GUI.
 */
class ConfigEffectItem_Menu : public ConfigEffectItem {
public:
  ConfigEffectItem_Menu(QString menuName);
  void type();

  QString menuName;
};
/**
 * Represents a gestural pie action effect item in the GUI.
 */
class ConfigEffectItem_Gestures : public ConfigEffectItem {
public:
  ConfigEffectItem_Gestures(QString menuName, QString cursorSetName);
  void type();

  QString menuName, cursorSetName;
};

/**
 * Provides the model for the GUI operations. Contains helper methods to set
 * various items in the config. Also serves as a YAML emitter.
 */
class Config {
public:
  Config();
  ~Config();

  /**
   * Creates a new blank configuration. Deletes all the stored data.
   */
  void createBlankConfig();

  /**
   * Specifies the configdata struct from edgy. This is used when loading an
   * existing config.
   */
  void useConfig(configdata* otherdata);

  // cursors stuff
  
  /**
   * Adds a cursor set to the config.
   * @param setName name of the cursor set.
   * @param cursors list of cursor names without the XK_ prefix.
   */
  void addCursorSet(QString setName, QStringList cursors);
  /**
   * Deletes a cursor set from this config.
   * @param setName name of the set.
   */
  void deleteCursorSet(QString setName);
  /**
   * Renames a cursor set in the current config.  Note that there may be
   * effects referencing this set, so they have to be updated separately.
   * @param oldSetName old set name.
   * @param newSetName new set name.
   */
  void renameCursorSet(QString oldSetName, QString newSetName);
  /**
   * Returns a list of cursor names in the set named setName.
   * @param setName name of the set.
   */
  QStringList cursors(QString setName);
  /**
   * Returns a list of cursor set names.
   */
  QStringList cursorSets();

  // menus stuff
  
  /**
   * Set a key action menu item in this config at a specific position.
   * Overwrites the existing item at that position (unless -1).
   * @param menuName name of the menu.
   * @param label key action label.
   * @param keys list of keys to press (ie. {k1, k2, k3})
   * @param place name of the screen position to move the cursor to. Could be
   *        empty.
   * @param pos position in the menu, where the item will be inserted. If pos
   *        is -1, the item is inserted at the end of the list. If pos is >= 0,
   *        the item at pos is replaced with the new one.
   */
  void setMenuItem_Key(QString menuName, QString label, QStringList keys, QString place, int pos);
  /**
   * Set a command action menu item in this config at a specific position.
   * @param menuName name of the menu that should contain this item.
   * @param label label of this item.
   * @param position of the item in the list.
   * @see setMenuItem_Key
   */
  void setMenuItem_Cmd(QString menuName, QString label, QString cmd, int pos);
  /**
   * Set a "no action" action menu item in this config at a specific position.
   * @param menuName name of the menu that should contain this item.
   * @param label label of this item.
   * @param position of the item in the list.
   * @see setMenuItem_Key
   */
  void setMenuItem_NoAction(QString menuName, QString label, int pos);
  /**
   * Retuns a list of menu items in a menu suitable for displaying in the GUI.
   * This is used to populate the table of menu items.
   * @param menuName name of the menu.
   * @return list of label/action pairs. Both the label and the action are
   *         in their string representation.
   */
  QList<QPair<QString, QString> > menuItems(QString menuName);
  
  /**
   * Returns a ConfigMenuItem object that represents the item in a menu.
   * @param menuName name of the menu to fetch the item from.
   * @param pos position of the item in the menu.
   * @return the menu item, or NULL if not found.
   */
  ConfigMenuItem* menuItem(QString menuName, int pos);
  /**
   * Returns a list of menu names.
   * @return list of menu names.
   */
  QStringList menus();
  /**
   * Deletes a menu item from a menu.
   * @param menuName name of the menu to delete the item from.
   * @param pos position of the item to delete in the menu.
   */
  void deleteMenuItem(QString menuName, int pos);
  /**
   * Rename a menu. Note that some effects could have been using this menu, so
   * they will need to be updated as well.
   * @param oldMenuName name of the old menu.
   * @param newMenuName name of the new menu.
   */
  void renameMenu(QString oldMenuName, QString newMenuName);
  /**
   * Delete a menu. Note that some effects could have been
   * using this menu, so they will need to be updated as well.
   * @param menu name of the menu to delete.
   */
  void deleteMenu(QString menu);

  // effects stuff
  
  /**
   * Creates a new Trigger object (from edgy.h) using the provided information.
   * This object can then be passed to the functions below.
   * @param button name of the button.
   * @param place name of the place on the screen.
   * @param fclass name of the focus (window) class.
   */
  Trigger* createTrigger(QString button, QString place, QString fclass);
  /**
   * Sets the key action effect item in this config at a specific position.
   * @param trigger the effect's trigger.
   * @param keys list of keys for the action. {k1, k2, k3}
   * @param place name of the place to move the cursor to.
   * @param pos position in the effect to insert the item.
   * @see setMenuItem_Key
   */
  void setEffect_Key(Trigger* trigger, QStringList keys, QString place, int pos);
  /**
   * Sets the stateful key action effect item in this config at a position.
   * @see setEffect_Key
   */
  void setEffect_SKey(Trigger* trigger, QStringList keys, QStringList buttons, int pos);
  /**
   * Sets the command action effect item in this config at a position.
   * @see setEffect_Key
   */
  void setEffect_Cmd(Trigger* trigger, QString cmd, int pos);
  /**
   * Sets the vertical menu action effect item in this config at a position.
   * @see setEffect_Key
   */
  void setEffect_Menu(Trigger* trigger, QString menuName, int pos);
  /**
   * Sets the gestural pie action effect item in this config at a position.
   * @see setEffect_Key
   */
  void setEffect_Gesture(Trigger* trigger, QString menuName, QString cursorsName, int pos);
  /**
   * Deletes an effect item from this config.
   * @param pos position in the effect list of the item.
   */
  void deleteEffectItem(int pos);
  /**
   * Returns a list of effect names in this config.
   * @return list of effect names.
   */
  QList<QStringList> effects();
  /**
   * Returns a ConfigEffectItem that represents the effect at the specified
   * position.
   * @param pos position in the effects list of the item.
   * @return the item at the position or NULL, if could not be found.
   */
  ConfigEffectItem* effectItem(int pos);
  /**
   * Returns the window class of the effect at the specified position.
   * @param pos position of the effect in the list.
   * @return the window (focus) class of the effect.
   */
  QString effectWindowClass(int pos);
  /**
   * Returns the {mouse button, area} pair of strings describing the effect
   * at the specified position in the effects list.
   * @param pos position of the effect in the effects list.
   * @return pair of mouse button and area strings.
   */
  QPair<QString, QString> effectArea(int pos);
  /**
   * Locates the effect with the specified vertical menu.
   * @param menuName name of the sought vertical menu.
   * @return position of the effect in the effects list with the matching
   *         menu or -1 if not found.
   */
  int findEffectWithMenu(QString menuName);
  /**
   * Locates the effect with the specified cursor set.
   * @param setName name of the sought cursor set.
   * @return position of the effect in the effects list with the matching
   *         menu or -1 if not found.
   */
  int findEffectWithCursorSet(QString setName);
  /**
   * Renames the menu in the specified effect.
   * @param pos position of the effect in the effects list.
   * @param newName the new name of the menu.
   */
  void renameMenuInEffect(int pos, QString newName);
  /**
   * Renames the cursor set in the specified effect.
   * @param pos position of the effect in the effects list.
   * @param newName the new name of the set.
   */
  void renameCursorSetInEffect(int pos, QString newName);

  // config emitter stuff
  
  /**
   * Sets the filename, where the config is to be saved.
   * @param filename absolute or relative file name.
   */
  void setDestinationFile(QString filename);
  /**
   * Returns the file name of the configuration file last set with
   * setDestinationFile().
   */
  QString destination();
  /**
   * Returns true if the destination file has been set.
   */
  bool destinationIsSet();
  /**
   * Attempts to write the config to the destination file (it should be set
   * before calling this with setDestinationFile()).
   * @return empty string if no error occurred while writing; exception
   *         message if there was an error.
   */
  QString writeConfig();

private:
  /**
   * Clears the cursors, effects and menus from the current config structure.
   */
  void cleanData();

  // indents in the YAML file
  int indentLevel();
  void incrIndent(int);
  QString addIndent();

  // helpers
  void addMenuItem(string& menuName, menu_item* mi, int pos);
  void addEffect(effect* ef, int pos);
  const Place* getPlace(QString place);
  keylist getKeys(QStringList keys);
  unsigned int getButtonFromString(QString button);
  QString getStringFromButton(unsigned int button);
  menu_item* getMenu(QString menuName);
  QString parseAction(Action* ptr);
  QString findMenuName(menu_item* items);

  configdata *data;
  QString filename;
  int indent;
};

#endif	/* _CONF_H */

